//visRayCasting.h
// Description:
//
// The raycasting class implements the simplest volume rendering.
// 1. First of all, only one image is generated when the program 
//    is run once, that is, there is not any interactivities;
//
//    The version including interactivity may be added when I have
//    an idea how to implement it. Therefor, attention should be 
//    given in this version for later compatability;
// 
// Some implementation details:
// 1. The data field is abstractly considered as a cube which is aligned
//    with the direction of eye sight;
// 2. Along the direction of eye sight the cube is resampled.
//

#ifndef VIS_RAY_CASTING_H
#define VIS_RAY_CASTING_H

#include "transferFunction.h"

namespace visualization
{
// This defines a pixel of image.
typedef unsigned char image[3];

class visRayCasting
{
public:
	visRayCasting(Model *md) : dataField(md)
	{
		transFunc.setDataField(md);
	}
	visRayCasting()
	{}
	~visRayCasting()
	{
		if(rayCastingImage)
		{
			for(int i = 0; i < imgSize; i++)
			{
				delete []rayCastingImage[i];
			}
			delete []rayCastingImage;
		}
		if(!pntsOnLight)
		{
			const int pointN = smpRate*imgSize*imgSize;
			for(int i = 0; i < pointN; i++)
			{
				delete []pntsOnLight[i];
			}
			delete pntsOnLight;
		}
		if(!sampledPoints)
		{
			delete []sampledPoints;
		}
	}
	void setModel(Model *md)
	{
		dataField = md;
		transFunc.setDataField(md);
	}
	
	// Generate the transfer function that will be used.
	void generateTransferFunction()
	{
//		transFunc.inputThresholds();
		transFunc.inputThresholdsByValue();
	}
	// Set the size of the image, with a default size "imageSize" by "imageSize"
	void setImageSize(int size = imageSize);

	void setSamplingRate(const int rate = samplingRate)
	{
		smpRate = rate;
	}

	// First, get the center "center" and the cube length "edge_lenght" of the data field.
	void computeBoundingCube();

	// Set the direction of light.
	// This direction points to the origin.
	void setViewingPosition(double dirction[], int dim);

	// Compute the direction of the light that points to the center
	// of the cube. This should be invoked after "setLightDirection(...)"
	void computeDirectionToCenter();

	// Resampling the data field and save the result in rayCastingImage.
	void resample();
	
	// Get image size.
	int getImageSize()
	{
		return imgSize;
	}
	void setImage(int p)
	{
		for(int i = 0; i < imgSize; i++)
			for(int j = 0; j < imgSize; j++)
				rayCastingImage[i][j][0] = rayCastingImage[i][j][1] = rayCastingImage[i][j][2] = p;
	}
	// Get the resulted image and display it later.
	image **getImage()
	{
		return rayCastingImage;
	}

	// Function used internally.
	//
	// The function receives an array(the first parameter) that indicates a direciotn, 
	// and returns an array(the second parameter) of the same size which is also a di-
	// rection and orthogonal to the input parameter.
	// Note that the default dimension is 3 and the result is normalized.
	void getOrthogonalDirection(double direction[], double tmpDir[]);

	// Crossmultiply. The first two vectors(in fact, directions) are used to calculate
	// the crossmultiply, with the result saved in the last parameter. The result is 
	// also normalized.
	void crossMultiply(double direction1[], double direction2[], double result[]);

	// Normalized a non-zero vector
	void normalize(double vec[3], double result[]);

	// This function advances a point(point) a specified length(length) along the direction
	// (direction) given, with the result in result.
	// The default dimension processed is 3.
	// Note that the parameter "flag" is used to indicate whether to advance along the po-
	// sotive of the direction or the negative.
	inline void advanceAPoint(const double point[], const double direction[], const double flag, const double length, double result[3]);

	// To form the ultimate image.
	void combineToFormImage(const int dir);

	// Compute the gradient.
	bool computeGradient(double &component, const double step, const int flag, const double centralPoint[3]);
	
	double **getThePointsPos()
	{
		return pntsOnLight;
	}
	double *getPointsValue()
	{
		return sampledPoints;
	}
private:
	// The data field.
	Model *dataField;

	// The direction of light which points to the origin. 
	// It should be fingured out that, currently, this variable is only used to indecate a direction but 
	// it's attribute as a vector of positoin is not used. 
	// In future, the position information may be used to more effectivly view the data field.
	//
	// Also, according to the current design, the variable should not coincide with the variable "center".
	// Because this will lead to the cituation that the direction_to_center can not be computed.
	double direction_to_origin[3];
	// The direction of light which points to the center of the data field. The direction from the viewint 
	// position to the center of the data field.
	double direction_to_center[3]; 
	
	// The center of the data field.
	double center[3];
	// The length of the data field "cube".
	double edge_length;

	// The size of the image.
	static const int imageSize = 256;
	int imgSize;
	//unsigned char rayCastingImage[imageSize][imageSize][color_RGB];
	
	// Now the image size can be dynamically determined.
	image **rayCastingImage;

	// Sampling rate, that is, the number of sample points along a single light.
	static const int samplingRate = 250;
	int smpRate;

	// The position information of the sampled points.
	double **pntsOnLight;
	// To save the value of the sampled points.
	double *sampledPoints;

	// The transfer function class.
	visTransferFunction transFunc;

	double opacity, opacityThreshold;
};

}
#endif

